package cc.facesign.admin.service.impl;

import cc.facesign.admin.bean.DeptTree;
import cc.facesign.admin.bean.TblDept;
import cc.facesign.admin.bean.vo.DeptVo;
import cc.facesign.admin.enums.DeptStatusType;
import cc.facesign.admin.mapper.TblDeptMapper;
import cc.facesign.admin.service.TblDeptService;
import cc.facesign.admin.service.TblManageDeptService;
import cc.facesign.admin.service.TblUserService;
import cc.facesign.common.exception.FacesignException;
import cc.facesign.common.utils.bean.R;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author testjava
 * @since 2022-05-06
 */
@Service
@Transactional
public class TblDeptServiceImpl extends ServiceImpl<TblDeptMapper, TblDept> implements TblDeptService {



    @Autowired
    private TblUserService userService;

    @Autowired
    private TblManageDeptService manageDeptService;

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public TblDept getRootByUserId(String id) {
        String rootId = userService.getRootDetpIdByUserId(id);
        return getById(rootId);
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public DeptVo getInfoById(String id) {
        DeptVo vo = baseMapper.selectInfoById(id);

        String s = vo.getPid();

        String pid;
        String[] split = s.split(",");
        if (split.length == 1) {
            pid = split[0];
        } else {
            pid = split[split.length - 2];
        }

        vo.setPName(getById(pid).getName());

        String status = vo.getStatus();
        String text = DeptStatusType.codeOf(Integer.valueOf(status));
        vo.setStatusText(text);

        return vo;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public List<TblDept> getRootList() {
        List<TblDept> list = baseMapper.getRootList();
        return list;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public List<DeptVo> getListByRootId(String id) {
        QueryWrapper<TblDept> w = new QueryWrapper<>();
        w.eq("rid", id);

        if (id.equals("0")) w = null; // admin部门特判

        List<String> ids = list(w).stream()
                .map(TblDept::getId)
                .collect(Collectors.toList());

        return getInfoBatch(ids);
    }

    private DeptTree getTreeByRootId(String id) {
        List<DeptVo> list = getListByRootId(id);
        if (list == null) {
            throw new FacesignException(R.ERROR, "无此根部门");
        }

        Map<String ,DeptVo> map = new HashMap<>();
        for (DeptVo e : list) {
            map.put(e.getId(), e);
        }

        DeptTree tree = new DeptTree();
        tree.setDept(map.get(id)); // 存root
        getTreeByRootId(map, tree);

        return tree;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public DeptTree getTreeByDeptId(String id) {
        TblDept dept = getById(id);
        String rid = dept.getRid();

        DeptTree root = getTreeByRootId(rid);

        DeptTree tree = getTreeByDeptId(root, id);

        Queue<DeptTree> queue = new LinkedList<>();
        queue.add(tree);

        while (!queue.isEmpty()) {
            DeptTree poll = queue.poll();
            DeptVo vo = poll.getDept();
            BeanUtils.copyProperties(vo, poll);

            if (poll.getChildren() != null) queue.addAll(poll.getChildren());
        }

        return tree;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public List<DeptVo> getChildListByDeptId(String id) {
        DeptTree tree = getTreeByDeptId(id);
        List<DeptVo> list = new ArrayList<>();
        getChildListByDeptId(list, tree);
        return list;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public List<DeptVo> getInfoBatch(List<String> ids) {
        List<DeptVo> list = baseMapper.getInfoBatch(ids);
        for (DeptVo vo : list) {
            String status = vo.getStatus();
            String text = DeptStatusType.codeOf(Integer.valueOf(status));
            vo.setStatusText(text);
        }
        return list;
    }

    @CacheEvict(value = "_CACHE::dept", allEntries = true)
    @Override
    public String addChild(DeptVo vo) {
        TblDept dept = new TblDept();
        BeanUtils.copyProperties(vo, dept);

        TblDept pDept = getById(dept.getPid());
        if (pDept == null) {
            throw new FacesignException(R.ERROR, "pid异常");
        }

        // 设置id
        String id = UUID.randomUUID().toString().replaceAll("-","");
        dept.setId(id);

        // 设置pid
        dept.setPid(pDept.getPid()+','+id);

        // 设置rid与父部门的rid相同
        dept.setRid(pDept.getRid());

        save(dept);
        return id;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public Set<String> getChildIdSetByDeptId(String id) {
        List<DeptVo> list = getChildListByDeptId(id);
        return list.stream()
                .map(DeptVo::getId)
                .collect(Collectors.toSet());
    }

    @CacheEvict(value = "_CACHE::dept", allEntries = true)
    @Override
    public String addRoot(DeptVo vo) {
        TblDept dept = new TblDept();
        BeanUtils.copyProperties(vo, dept);

        String id = UUID.randomUUID().toString().replaceAll("-", "");
        dept.setId(id);
        dept.setRid(id);
        dept.setPid("0,"+id);

        save(dept);

        return id;
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]+','+#root.args[1]")
    @Override
    public String getIdByDeptNameAndRootName(String deptName, String rootDeptName) {
        return baseMapper.getIdByDeptNameAndRootName(deptName, rootDeptName);
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public DeptVo getPDeptVo(String deptId) {

        TblDept dept = getById(deptId);
        if (deptId.equals("0")) return getInfoById("0");

        String[] split = dept.getPid().split(",");
        String pid = split[split.length - 2];
        return getInfoById(pid);
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]")
    @Override
    public String getRid(String deptId) {
        return getById(deptId).getRid();
    }

    @Cacheable(value = "_CACHE::dept", key="#root.methodName+':'+#root.args[0]+','+#root.args[1]")
    @Override
    public int compareGrade(String deptId1, String deptId2) {
        DeptTree tree = getTreeByDeptId("0");

        Queue<DeptTree> queue = new LinkedList<>();
        queue.add(tree);

        int d = 0;
        int r1 = 0, r2 = 0;
        while (!queue.isEmpty()) {
            DeptTree poll = queue.poll();

            List<DeptTree> children = poll.getChildren();
            if (children != null) {
                queue.addAll(children);
                d++;
                for (DeptTree e : children) {
                    String cid = e.getId();
                    if (cid.equals(deptId1)) r1 = d;
                    if (cid.equals(deptId2)) r2 = d;
                }
            }
        }
        return Integer.compare(r2, r1);
    }

    private void getChildListByDeptId(List<DeptVo> list, DeptTree tree) {
        Queue<DeptTree> queue = new LinkedList<>();
        queue.add(tree);

        while (!queue.isEmpty()) {
            DeptTree poll = queue.poll();
            list.add(poll.getDept());
            if (poll.getChildren() != null)
                queue.addAll(poll.getChildren());
        }
    }

    private DeptTree getTreeByDeptId(DeptTree tree, String id) {
        Queue<DeptTree> queue = new LinkedList<>();
        queue.add(tree);

        while (!queue.isEmpty()) {
            DeptTree u = queue.poll();
            if (u.getDept().getId().equals(id)) return u;
            if (u.getChildren() != null) queue.addAll(u.getChildren());
        }

        return null;
    }

    private void getTreeByRootId(Map<String, DeptVo> map, DeptTree tree) {
        String id = tree.getDept().getId();

        for (Map.Entry<String, DeptVo> e : map.entrySet()) {
            DeptVo vo = e.getValue();

            if (id.equals(vo.getPid()) && !vo.getId().equals("0")) {
                if (tree.getChildren() == null) tree.setChildren(new ArrayList<>());

                DeptTree t = new DeptTree();
                t.setDept(vo);
                tree.getChildren().add(t);

                getTreeByRootId(map, t);
            }
        }
    }
}
